package com.lagou.edu.factory;

import com.lagou.edu.anno.Autowired;
import com.lagou.edu.anno.Component;
import com.lagou.edu.anno.Repository;
import com.lagou.edu.anno.Service;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.*;

public class BeanFactory {

    private static Map<String, Object> map = new HashMap<>();  // 存储对象

    static {
        List<Class<?>> classes = new LinkedList<>();
        String packageName = "com.lagou.edu";
        ClassLoader classLoader = BeanFactory.class.getClassLoader();
        try {
            Enumeration<URL> resources = classLoader.getResources("com/lagou/edu");
            while (resources.hasMoreElements()) {
                URL url = resources.nextElement();
                String fileWithPath = url.getFile();
                File file = new File(fileWithPath);
                if (file.isFile()) {
                    String className = file.getName()
                            .substring(0, file.getName()
                                    .length() - 6);
                    classes.add(Class.forName(packageName + "." + className));
                } else {
                    findClassesInPackage(packageName, file, classes, classLoader);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        for (Class<?> aClass : classes) {
            if (aClass.isAnnotationPresent(Service.class)) {
                Service service = aClass.getAnnotation(Service.class);
                createBean(aClass, service.value());
            } else if (aClass.isAnnotationPresent(Repository.class)) {
                Repository repository = aClass.getAnnotation(Repository.class);
                createBean(aClass, repository.value());
            } else if (aClass.isAnnotationPresent(Component.class)) {
                Component component = aClass.getAnnotation(Component.class);
                createBean(aClass, component.value());
            }
        }
        try {
            for (Map.Entry<String, Object> entry : map.entrySet()) {
                Class<?> aClass = entry.getValue()
                        .getClass();
                Field[] fields = aClass.getDeclaredFields();
                for (Field field : fields) {
                    if (field.isAnnotationPresent(Autowired.class)) {
                        field.setAccessible(true);
                        Class<?> autowiredBeanClass = field.getDeclaringClass();
                        String id = field.getName();
                        if (map.get(id) == null) {
                            String simpleName = autowiredBeanClass.getSimpleName();
                            id = simpleName.substring(0, 1)
                                    .toLowerCase() + simpleName.substring(1);

                        }
                        field.set(entry.getValue(), map.get(id));

                    }
                }
                map.put(entry.getKey(), entry.getValue());
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    private static void createBean(Class<?> aClass, String value) {
        String id;
        if (value != null && value.length() > 0) {
            id = value;
        } else {
            String simpleName = aClass.getSimpleName();
            id = simpleName.substring(0, 1)
                    .toLowerCase() + simpleName.substring(1);
        }
        Object o = null;  // 实例化之后的对象
        try {
            o = aClass.newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        // 存储到map中待用
        map.put(id, o);
    }

    private static void findClassesInPackage(String packageName, File directory, List<Class<?>> classes, ClassLoader classLoader) throws IOException, ClassNotFoundException {
        File[] files = directory.listFiles(file -> {
            boolean isDirectory = file.isDirectory();
            boolean classFile = file.getName()
                    .endsWith(".class");
            return isDirectory || classFile;
        });
        for (File file : files) {
            if (file.isDirectory()) {
                findClassesInPackage(packageName + "." + file.getName(), file, classes, classLoader);
            } else {
                String className = file.getName()
                        .substring(0, file.getName()
                                .length() - 6);
                classes.add(Class.forName(packageName + "." + className));
            }
        }
    }

    public static Object getBean(String id) {
        return map.get(id);
    }

}
